package org.convallaria.system.biz.service.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.convallaria.system.biz.service.SecurityMonitoringService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 安全事件监控服务实现类
 * 
 * @author convallaria
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SecurityMonitoringServiceImpl implements SecurityMonitoringService {

    private final RedisTemplate<String, Object> redisTemplate;
    
    private static final String SECURITY_EVENT_PREFIX = "security:event:";
    private static final String IP_BLACKLIST_PREFIX = "security:blacklist:ip:";
    private static final String USER_SECURITY_SCORE_PREFIX = "security:score:user:";
    private static final String ABNORMAL_LOGIN_PREFIX = "security:abnormal:login:";
    private static final String SECURITY_CONFIG_KEY = "security:config:monitoring";

    @Override
    public void recordSecurityEvent(String eventType, Long userId, String ip, String details, String severity) {
        try {
            String eventId = UUID.randomUUID().toString();
            Map<String, Object> event = new HashMap<>();
            event.put("eventId", eventId);
            event.put("eventType", eventType);
            event.put("userId", userId);
            event.put("ip", ip);
            event.put("details", details);
            event.put("severity", severity);
            event.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            
            // 存储到Redis，设置24小时过期
            String key = SECURITY_EVENT_PREFIX + eventId;
            redisTemplate.opsForValue().set(key, event, 24, TimeUnit.HOURS);
            
            // 记录到日志
            log.warn("安全事件记录: type={}, userId={}, ip={}, severity={}, details={}", 
                    eventType, userId, ip, severity, details);
            
            // 如果是高风险事件，立即发送告警
            if ("HIGH".equals(severity) || "CRITICAL".equals(severity)) {
                sendSecurityAlert("HIGH_RISK_EVENT", 
                    String.format("高风险安全事件: %s - %s", eventType, details), 
                    Arrays.asList("admin@convallaria.com"));
            }
            
        } catch (Exception e) {
            log.error("记录安全事件失败", e);
        }
    }

    @Override
    public Map<String, Object> getRealTimeSecurityStats(int minutes) {
        Map<String, Object> stats = new HashMap<>();
        
        try {
            LocalDateTime startTime = LocalDateTime.now().minusMinutes(minutes);
            String pattern = SECURITY_EVENT_PREFIX + "*";
            
            Set<String> keys = redisTemplate.keys(pattern);
            int totalEvents = 0;
            int highRiskEvents = 0;
            int criticalEvents = 0;
            Map<String, Integer> eventTypeCount = new HashMap<>();
            
            if (keys != null) {
                for (String key : keys) {
                    Map<String, Object> event = (Map<String, Object>) redisTemplate.opsForValue().get(key);
                    if (event != null) {
                        String timestamp = (String) event.get("timestamp");
                        LocalDateTime eventTime = LocalDateTime.parse(timestamp, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
                        
                        if (eventTime.isAfter(startTime)) {
                            totalEvents++;
                            String severity = (String) event.get("severity");
                            String eventType = (String) event.get("eventType");
                            
                            if ("HIGH".equals(severity)) {
                                highRiskEvents++;
                            } else if ("CRITICAL".equals(severity)) {
                                criticalEvents++;
                            }
                            
                            eventTypeCount.merge(eventType, 1, Integer::sum);
                        }
                    }
                }
            }
            
            stats.put("totalEvents", totalEvents);
            stats.put("highRiskEvents", highRiskEvents);
            stats.put("criticalEvents", criticalEvents);
            stats.put("eventTypeCount", eventTypeCount);
            stats.put("timeRange", minutes + "分钟");
            
        } catch (Exception e) {
            log.error("获取实时安全统计失败", e);
            stats.put("error", "获取统计数据失败");
        }
        
        return stats;
    }

    @Override
    public List<Map<String, Object>> getHighRiskEvents(int limit) {
        List<Map<String, Object>> highRiskEvents = new ArrayList<>();
        
        try {
            String pattern = SECURITY_EVENT_PREFIX + "*";
            Set<String> keys = redisTemplate.keys(pattern);
            
            if (keys != null) {
                List<Map<String, Object>> allEvents = new ArrayList<>();
                
                for (String key : keys) {
                    Map<String, Object> event = (Map<String, Object>) redisTemplate.opsForValue().get(key);
                    if (event != null) {
                        String severity = (String) event.get("severity");
                        if ("HIGH".equals(severity) || "CRITICAL".equals(severity)) {
                            allEvents.add(event);
                        }
                    }
                }
                
                // 按时间排序，最新的在前
                allEvents.sort((a, b) -> {
                    String timeA = (String) a.get("timestamp");
                    String timeB = (String) b.get("timestamp");
                    return timeB.compareTo(timeA);
                });
                
                // 限制返回数量
                int count = Math.min(limit, allEvents.size());
                for (int i = 0; i < count; i++) {
                    highRiskEvents.add(allEvents.get(i));
                }
            }
            
        } catch (Exception e) {
            log.error("获取高风险事件失败", e);
        }
        
        return highRiskEvents;
    }

    @Override
    public boolean isIpBlacklisted(String ip) {
        try {
            String key = IP_BLACKLIST_PREFIX + ip;
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.error("检查IP黑名单失败: ip={}", ip, e);
            return false;
        }
    }

    @Override
    public void addIpToBlacklist(String ip, String reason, int duration) {
        try {
            String key = IP_BLACKLIST_PREFIX + ip;
            Map<String, Object> blacklistInfo = new HashMap<>();
            blacklistInfo.put("ip", ip);
            blacklistInfo.put("reason", reason);
            blacklistInfo.put("addTime", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            blacklistInfo.put("duration", duration);
            
            redisTemplate.opsForValue().set(key, blacklistInfo, duration, TimeUnit.HOURS);
            
            log.warn("IP已加入黑名单: ip={}, reason={}, duration={}小时", ip, reason, duration);
            
            // 记录安全事件
            recordSecurityEvent("IP_BLACKLISTED", null, ip, 
                "IP加入黑名单: " + reason, "HIGH");
                
        } catch (Exception e) {
            log.error("添加IP到黑名单失败: ip={}", ip, e);
        }
    }

    @Override
    public void removeIpFromBlacklist(String ip) {
        try {
            String key = IP_BLACKLIST_PREFIX + ip;
            redisTemplate.delete(key);
            
            log.info("IP已从黑名单移除: ip={}", ip);
            
            // 记录安全事件
            recordSecurityEvent("IP_WHITELISTED", null, ip, 
                "IP从黑名单移除", "MEDIUM");
                
        } catch (Exception e) {
            log.error("从黑名单移除IP失败: ip={}", ip, e);
        }
    }

    @Override
    public Map<String, Object> getAbnormalLoginStats(int hours) {
        Map<String, Object> stats = new HashMap<>();
        
        try {
            LocalDateTime startTime = LocalDateTime.now().minusHours(hours);
            String pattern = ABNORMAL_LOGIN_PREFIX + "*";
            
            Set<String> keys = redisTemplate.keys(pattern);
            int abnormalCount = 0;
            Map<String, Integer> ipCount = new HashMap<>();
            Map<String, Integer> userCount = new HashMap<>();
            
            if (keys != null) {
                for (String key : keys) {
                    Map<String, Object> abnormal = (Map<String, Object>) redisTemplate.opsForValue().get(key);
                    if (abnormal != null) {
                        String timestamp = (String) abnormal.get("timestamp");
                        LocalDateTime abnormalTime = LocalDateTime.parse(timestamp, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
                        
                        if (abnormalTime.isAfter(startTime)) {
                            abnormalCount++;
                            String ip = (String) abnormal.get("ip");
                            Long userId = (Long) abnormal.get("userId");
                            
                            ipCount.merge(ip, 1, Integer::sum);
                            if (userId != null) {
                                userCount.merge(userId.toString(), 1, Integer::sum);
                            }
                        }
                    }
                }
            }
            
            stats.put("abnormalCount", abnormalCount);
            stats.put("ipCount", ipCount);
            stats.put("userCount", userCount);
            stats.put("timeRange", hours + "小时");
            
        } catch (Exception e) {
            log.error("获取异常登录统计失败", e);
            stats.put("error", "获取统计数据失败");
        }
        
        return stats;
    }

    @Override
    public boolean detectAbnormalLogin(Long userId, String ip, String userAgent) {
        try {
            // 检查IP是否在黑名单中
            if (isIpBlacklisted(ip)) {
                recordSecurityEvent("BLACKLISTED_IP_LOGIN", userId, ip, 
                    "黑名单IP尝试登录", "CRITICAL");
                return true;
            }
            
            // 检查是否在短时间内多次登录失败
            String loginAttemptKey = "login:attempt:" + userId + ":" + ip;
            Integer attemptCount = (Integer) redisTemplate.opsForValue().get(loginAttemptKey);
            if (attemptCount != null && attemptCount >= 5) {
                recordSecurityEvent("MULTIPLE_LOGIN_FAILURES", userId, ip, 
                    "短时间内多次登录失败", "HIGH");
                return true;
            }
            
            // 检查是否异常时间登录（凌晨2-6点）
            int hour = LocalDateTime.now().getHour();
            if (hour >= 2 && hour <= 6) {
                recordSecurityEvent("ABNORMAL_TIME_LOGIN", userId, ip, 
                    "异常时间登录", "MEDIUM");
                return true;
            }
            
            // 检查是否异常地理位置登录（这里简化处理，实际应该根据IP地理位置判断）
            String geoKey = "user:geo:" + userId;
            String lastGeo = (String) redisTemplate.opsForValue().get(geoKey);
            if (lastGeo != null && !lastGeo.equals(ip)) {
                // 这里简化处理，实际应该根据IP地理位置判断
                recordSecurityEvent("ABNORMAL_LOCATION_LOGIN", userId, ip, 
                    "异常地理位置登录", "HIGH");
                return true;
            }
            
            // 更新用户地理位置
            redisTemplate.opsForValue().set(geoKey, ip, 7, TimeUnit.DAYS);
            
            return false;
            
        } catch (Exception e) {
            log.error("检测异常登录失败: userId={}, ip={}", userId, ip, e);
            return false;
        }
    }

    @Override
    public Map<String, Object> getSecurityAlertConfig() {
        try {
            Map<String, Object> config = (Map<String, Object>) redisTemplate.opsForValue().get(SECURITY_CONFIG_KEY);
            if (config == null) {
                // 返回默认配置
                config = new HashMap<>();
                config.put("enableEmailAlert", true);
                config.put("enableSmsAlert", false);
                config.put("alertThreshold", 5);
                config.put("alertRecipients", Arrays.asList("admin@convallaria.com"));
            }
            return config;
        } catch (Exception e) {
            log.error("获取安全告警配置失败", e);
            return new HashMap<>();
        }
    }

    @Override
    public void updateSecurityAlertConfig(Map<String, Object> config) {
        try {
            redisTemplate.opsForValue().set(SECURITY_CONFIG_KEY, config);
            log.info("安全告警配置已更新: {}", config);
        } catch (Exception e) {
            log.error("更新安全告警配置失败", e);
        }
    }

    @Override
    public void sendSecurityAlert(String alertType, String message, List<String> recipients) {
        try {
            // 这里简化处理，实际应该集成邮件/短信服务
            log.warn("安全告警 [{}]: {} -> {}", alertType, message, recipients);
            
            // 记录告警事件
            recordSecurityEvent("SECURITY_ALERT", null, null, 
                String.format("告警类型: %s, 消息: %s", alertType, message), "HIGH");
                
        } catch (Exception e) {
            log.error("发送安全告警失败: alertType={}, message={}", alertType, message, e);
        }
    }

    @Override
    public int getUserSecurityScore(Long userId) {
        try {
            String key = USER_SECURITY_SCORE_PREFIX + userId;
            Integer score = (Integer) redisTemplate.opsForValue().get(key);
            
            if (score == null) {
                // 计算用户安全评分
                score = calculateUserSecurityScore(userId);
                redisTemplate.opsForValue().set(key, score, 1, TimeUnit.HOURS);
            }
            
            return score;
        } catch (Exception e) {
            log.error("获取用户安全评分失败: userId={}", userId, e);
            return 50; // 默认中等评分
        }
    }

    @Override
    public Map<String, Object> getSystemSecurityOverview() {
        Map<String, Object> overview = new HashMap<>();
        
        try {
            // 获取实时统计
            Map<String, Object> realTimeStats = getRealTimeSecurityStats(60);
            overview.put("realTimeStats", realTimeStats);
            
            // 获取高风险事件
            List<Map<String, Object>> highRiskEvents = getHighRiskEvents(10);
            overview.put("highRiskEvents", highRiskEvents);
            
            // 获取异常登录统计
            Map<String, Object> abnormalStats = getAbnormalLoginStats(24);
            overview.put("abnormalStats", abnormalStats);
            
            // 获取黑名单IP数量
            String pattern = IP_BLACKLIST_PREFIX + "*";
            Set<String> blacklistKeys = redisTemplate.keys(pattern);
            overview.put("blacklistCount", blacklistKeys != null ? blacklistKeys.size() : 0);
            
            // 获取在线用户数量
            String onlinePattern = "user:online:*";
            Set<String> onlineKeys = redisTemplate.keys(onlinePattern);
            overview.put("onlineUserCount", onlineKeys != null ? onlineKeys.size() : 0);
            
        } catch (Exception e) {
            log.error("获取系统安全概览失败", e);
            overview.put("error", "获取概览数据失败");
        }
        
        return overview;
    }

    /**
     * 计算用户安全评分
     */
    private int calculateUserSecurityScore(Long userId) {
        int score = 100; // 基础分数
        
        try {
            // 检查登录失败次数
            String loginAttemptKey = "login:attempt:" + userId + ":*";
            Set<String> attemptKeys = redisTemplate.keys(loginAttemptKey);
            if (attemptKeys != null) {
                score -= attemptKeys.size() * 5; // 每次失败扣5分
            }
            
            // 检查异常登录次数
            String abnormalKey = ABNORMAL_LOGIN_PREFIX + userId;
            Integer abnormalCount = (Integer) redisTemplate.opsForValue().get(abnormalKey);
            if (abnormalCount != null) {
                score -= abnormalCount * 10; // 每次异常扣10分
            }
            
            // 检查密码强度（这里简化处理）
            // 实际应该检查密码复杂度、是否定期更换等
            
            // 确保分数在0-100范围内
            return Math.max(0, Math.min(100, score));
            
        } catch (Exception e) {
            log.error("计算用户安全评分失败: userId={}", userId, e);
            return 50;
        }
    }
}
